﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.VisualStudio.TemplateWizard;
using System.IO;
using System.Xml;
using Microsoft.Win32;
using System.Windows.Forms;
using System.Reflection;
using System.ComponentModel;
using EnvDTE80;
using EnvDTE;

namespace HG.INF.BizTalk.SolutionWizard
{
    public class SolutionWizard:IWizard
    {

        private static DTE2 dte;

        public static Dictionary<string, string> solutionDictionary;

        public void BeforeOpeningFile(EnvDTE.ProjectItem projectItem)
        {
            
        }

        public void ProjectFinishedGenerating(EnvDTE.Project project)
        {
            ///Get Target Dir 
            var targetdir = Environment.GetEnvironmentVariable("BTDFEXTENSIONPATH", EnvironmentVariableTarget.Machine);
            
            if (Directory.Exists(targetdir))
            {
                var projecttemplatedir = Path.Combine(targetdir, "ProjectTemplate");
                if (!Directory.Exists(projecttemplatedir))
                {
                    throw new ArgumentException(string.Format("Custom Template Directory {0} couldn't be fouund!", projecttemplatedir), "projecttemplatedir");

                    }


                if (dte == null)
                {
                    MessageBox.Show(
                        "Cannot convert automation object to DTE2.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                if (!dte.Solution.IsOpen)
                {
                    MessageBox.Show(
                        "Please open your BizTalk solution, then use the Add New Project dialog on the solution to add this project.",
                        "Error",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }

                string destinationPath = solutionDictionary["$destinationdirectory$"];
                

                if (string.IsNullOrEmpty(destinationPath))
                {
                    MessageBox.Show(
                        "Cannot determine destination directory ($destinationdirectory$ is missing).",
                        "Error",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }

              

                DeploymentOptions options = new DeploymentOptions();

                OptionsForm optionsFrm = new OptionsForm(options);
                if (optionsFrm.ShowDialog() != DialogResult.OK)
                {
                    return;
                }

                

                string[] templateFiles = Directory.GetFiles(projecttemplatedir, "*.*", SearchOption.AllDirectories);
                DirectoryInfo di = new DirectoryInfo(destinationPath).Parent;


                destinationPath = Path.Combine(di.FullName, "Deployment");
                try
                {
                    CopyDirectory(projecttemplatedir, destinationPath);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        "Failed to copy Custom Deployment Framework template files to destination folder: " + ex.Message,
                        "Error",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error);
                    return;
                }


                string solutionName = solutionDictionary["integrationname"];
                string integrationcode = solutionDictionary["integrationcode"];
                string projectFileName = "Deployment.btdfproj";
                string projectFilePath = Path.Combine(destinationPath, projectFileName);
                var dialogueresult = MessageBox.Show("Do you want to use the Environment Settings DB?",
                    "USEENVSETTINGSDB Parameter", MessageBoxButtons.YesNo);
                bool useenvsettingsdb = dialogueresult == DialogResult.Yes;

                Dictionary<string, string> replacements = new Dictionary<string, string>();
                replacements.Add("[PROJECTNAME]", solutionName);

                replacements.Add("[USEENVSETTINGSDB]", useenvsettingsdb.ToString());

                ReplaceInTextFile(destinationPath, "BuildDebugMsi.bat", replacements, Encoding.ASCII);
                ReplaceInTextFile(destinationPath, "BuildReleaseMsi.bat", replacements, Encoding.ASCII);
                ReplaceInTextFile(destinationPath, "InstallWizard.xml", replacements, Encoding.UTF8);
                ReplaceInTextFile(destinationPath, "UnInstallWizard.xml", replacements, Encoding.UTF8);

                replacements.Add("[GUID1]", Guid.NewGuid().ToString());
                replacements.Add("[GUID2]", Guid.NewGuid().ToString());
                replacements.Add("[INTEGRATIONCODE]", integrationcode);
                ReplaceInTextFile(destinationPath, projectFileName, replacements, Encoding.UTF8);

                UpdateProjectFile(projectFilePath, options, optionsFrm.WritePropertiesOnlyWhenNonDefault);

                DirectoryInfo destinationdir = new DirectoryInfo(destinationPath);

                Solution2 sol2 = (Solution2)dte.Solution;

                Project deployment = sol2.AddSolutionFolder("Deployment");


               

                foreach (var deploymentfile in destinationdir.GetFiles("*.*", SearchOption.AllDirectories))
                {
                    deployment.ProjectItems.AddFromFile(deploymentfile.FullName);
                }


                try
                {
                    dte.ExecuteCommand("File.OpenFile", '"' + projectFilePath + '"');
                }
                catch (Exception)
                {
                    MessageBox.Show("Failed to open .btdfproj file in editor.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }

                MessageBox.Show(
                    "A Hanze Deployment Framework for BizTalk project has been configured in " + destinationPath + ". You must edit " + projectFileName + " to configure your specific deployment requirements.",
                    "Project Created",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
             }
        }
        
        public void ProjectItemFinishedGenerating(EnvDTE.ProjectItem projectItem)
        {
            
        }

        public void RunFinished()
        {
            
        }

        public void RunStarted(object automationObject, Dictionary<string,string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
        {
           


            
            solutionDictionary = replacementsDictionary;
            dte = automationObject as  DTE2;

            string solutionname = replacementsDictionary["$projectname$"];
            solutionDictionary.Add("solutionname", solutionname);

            FrmSolutionWizard form = new FrmSolutionWizard() {IntegrationName = solutionname};
            form.ShowDialog();
            var integrationCode = form.IntegrationCode;
            var integrationName = form.IntegrationName;

            form.Dispose();
            
            solutionDictionary.Add("integrationcode", integrationCode);
           
            solutionDictionary.Add("solutiondir", replacementsDictionary["$destinationdirectory$"]);
            solutionDictionary.Add("integrationname", integrationName);
        }

        public bool ShouldAddProjectItem(string filePath)
        {

            System.Diagnostics.Debugger.Launch();
            return true;
        }


        #region privates

        private static void ReplaceInTextFile(
           string filePath, string fileName, Dictionary<string, string> replacements, Encoding encodingMode)
        {
            string combinedFilePath = null;
            string fileContents = null;

            combinedFilePath = Path.Combine(filePath, fileName);
            fileContents = File.ReadAllText(combinedFilePath, encodingMode);

            foreach (KeyValuePair<string, string> replacement in replacements)
            {
                fileContents = fileContents.Replace(replacement.Key, replacement.Value);
            }

            File.WriteAllText(combinedFilePath, fileContents, encodingMode);
        }

        // Copy directory structure recursively
        // From a CodeProject article by Richard Lopes  
        private static void CopyDirectory(string src, string dest)
        {
            string[] files;

            if (dest[dest.Length - 1] != Path.DirectorySeparatorChar)
            {
                dest += Path.DirectorySeparatorChar;
            }

            if (!Directory.Exists(dest))
            {
                Directory.CreateDirectory(dest);
            }

            files = Directory.GetFileSystemEntries(src);

            foreach (string element in files)
            {
                // Sub directories
                if (Directory.Exists(element))
                {
                    CopyDirectory(element, dest + Path.GetFileName(element));
                }
                // Files in directory
                else
                {
                    File.Copy(element, dest + Path.GetFileName(element), true);
                }
            }
        }

        private void UpdateProjectFile(string projectFilePath, DeploymentOptions options, bool writeOnlyWhenNonDefault)
        {
            XmlDocument projectXml = new XmlDocument();
            projectXml.Load(projectFilePath);

            XmlNamespaceManager xnm = new XmlNamespaceManager(projectXml.NameTable);
            xnm.AddNamespace(string.Empty, "http://schemas.microsoft.com/developer/msbuild/2003");
            xnm.AddNamespace("ns0", "http://schemas.microsoft.com/developer/msbuild/2003");

            XmlElement projectElement = projectXml.SelectSingleNode("/ns0:Project", xnm) as XmlElement;
            string bizTalkProductName = GetBizTalkProductName();

            if (string.Compare(bizTalkProductName, "Microsoft BizTalk Server 2010", true) == 0
                || string.Compare(bizTalkProductName, "Microsoft BizTalk Server 2013", true) == 0)
            {
                projectElement.SetAttribute("ToolsVersion", "4.0");
            }

            XmlElement generalPropertyGroup = projectXml.SelectSingleNode("/ns0:Project/ns0:PropertyGroup[1]", xnm) as XmlElement;

            Type doType = options.GetType();
            PropertyInfo[] doProperties = doType.GetProperties();

            foreach (PropertyInfo pi in doProperties)
            {
                if (pi.PropertyType.Equals(typeof(bool)))
                {
                    if (writeOnlyWhenNonDefault)
                    {
                        object[] dvAttribute = pi.GetCustomAttributes(typeof(DefaultValueAttribute), false);
                        DefaultValueAttribute dva = dvAttribute[0] as DefaultValueAttribute;
                        bool defaultValue = (bool)dva.Value;

                        bool propertyValue = (bool)pi.GetValue(options, null);

                        if (defaultValue != propertyValue)
                        {
                            WriteElementText(projectXml, xnm, generalPropertyGroup, pi.Name, propertyValue);
                        }
                    }
                    else
                    {
                        WriteElementText(projectXml, xnm, generalPropertyGroup, pi.Name, (bool)pi.GetValue(options, null));
                    }
                }
            }


            projectXml.Save(projectFilePath);
        }

        private void WriteElementText<T>(
            XmlDocument projectXml, XmlNamespaceManager xnm, XmlElement propertyGroup, string elementName, T elementValue)
        {
            XmlElement xe = propertyGroup.SelectSingleNode("ns0:" + elementName, xnm) as XmlElement;

            if (xe == null)
            {
                xe = projectXml.CreateElement(string.Empty, elementName, xnm.DefaultNamespace);
                xe.InnerText = elementValue.ToString();
                propertyGroup.AppendChild(xe);
            }
            else
            {
                xe.InnerText = elementValue.ToString();
            }
        }

        private string GetBizTalkProductName()
        {
            string bizTalkProduct =
                (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\BizTalk Server\3.0", "ProductName", null);

            if (string.IsNullOrEmpty(bizTalkProduct))
            {
                MessageBox.Show(
                    "Cannot find BizTalk Server install registry key.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }

            return bizTalkProduct;
        }
        #endregion
    }
}
